/*
 *	stringconcat.c
 *	© 2009 Tom Booth
 *
 *	Implementation of a string joiner using linked lists
 *
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>

#include "stringconcat.h"

// define the ammount of list that can be being created at a time
#define SC_MAXSIMUL 10


struct sc_list_item {
	char *value;
	int length;
	struct sc_list_item *next;
};

struct sc_list {
	int used;
	int length;
	struct sc_list_item *start;
};


// private functions
static void sc_delete(struct sc_list_item *item);


// a count of the number of occupied lists
static int count = 0;

// array of avaliable stirng creation spots
static struct sc_list lists[SC_MAXSIMUL];

// mutex and condition for waiting on being allowed a concat descriptor
pthread_mutex_t sc_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t sc_cond = PTHREAD_COND_INITIALIZER;

int sc_start(void) {
	int i;
	
	// wait until there is a spot free
	while (!(count<SC_MAXSIMUL)) { pthread_cond_wait(&sc_cond, &sc_mutex); }
	
	for (i=0; i<SC_MAXSIMUL; i++) {
		if (lists[i].used == 0) { 
			lists[i].used=1;
			count++;
			
			pthread_mutex_unlock(&sc_mutex);
			return i+1; 
		} 
	}
	
	return -1;
}


int sc_add(int id, const char* s) {
	
	struct sc_list_item *current;
	int i = id-1;
	
	#ifdef DF_DEBUG
	dferrors_printdebug("adding: %s\n", s);
	#endif
	
	if (lists[i].start==NULL) {
		lists[i].start = malloc(sizeof(struct sc_list_item));
		if (lists[i].start==NULL) return -1;
		
		lists[i].start->value = malloc((strlen(s)+1) * sizeof(char));
		if (lists[i].start->value==NULL) return -1;
		
		strcpy(lists[i].start->value, s);		
		lists[i].start->length = strlen(s);
		
		current = lists[i].start;
	}
	else {
		// find the current last item in the list
		current = lists[i].start;
		while (current->next!=NULL) {
			current = current->next;
		}
	
		// malloc another item and add it to the list
		current->next = malloc(sizeof(struct sc_list_item));
		if (current->next==NULL) return -1;
		
		current = current->next;
		
		current->value = malloc((strlen(s)+1) * sizeof(char));
		if (current->value==NULL) return -1;
		
		strcpy(current->value, s);
		
		#ifdef DF_DEBUG
		dferrors_printdebug("ss: %d   sss: %d\n", sizeof(s), sizeof(current->value));
		#endif
		
		current->length = strlen(s);
	}
	
	current->next = NULL;
	
	// add the length of the string to the counter
	lists[i].length+=current->length;
	
	return 0;
}


char* sc_join(int id) {
	
	char *ret;
	struct sc_list_item *current;
	unsigned int offset=0;
	int i = id-1;
	
	#ifdef DF_DEBUG
	dferrors_printdebug("joining: %d\n", length);
	#endif
	
	// malloc us a string of size length + 1 so we can add null terminator
	if ((ret = malloc((lists[i].length+1) * sizeof(char))) == NULL) {dferrors_printerror(-DFCLIENT_ENOMEM, "Failed to realloc the find buffer.");return NULL;}
	
	// loop through the linked list and copy them into the return val
	current = lists[i].start;
	while (current!=NULL) {	
		#ifdef DF_DEBUG
		dferrors_printdebug("copying: %s to: %u length: %d\n", current->value, offset, current.length);
		#endif
			
		strncpy(ret+offset, current->value, current->length);
		offset += current->length;
		
		current = current->next;
	}
	
	// add the null terminator
	ret[lists[i].length] = '\0';
	
	// return the generated cstring
	return ret;
}


void sc_clear(int id) {
	
	#ifdef DF_DEBUG
	dferrors_printdebug("clearing\n");
	#endif
	
	sc_delete(lists[id-1].start);
	
	lists[id-1].start=NULL;
	lists[id-1].length=0;
	lists[id-1].used=0;
	
	count--;
	
	pthread_cond_signal(&sc_cond);
}

void sc_delete(struct sc_list_item *item) {
	
	if (item==NULL) return;
	
	if (item->next != NULL) {
		sc_delete(item->next);
		item->next = NULL;
	}
	
	#ifdef DF_DEBUG
	dferrors_printdebug("deleting: %s length: %d\n", item->value, item.length);
	#endif
	
	free(item->value);
	free(item);
		
}

void sc_dump(int id) {
	struct sc_list_item *current;
	int i = 0;
	
	dferrors_printdebug("Dumping stringconcat list:\n");
	
	current = lists[id-1].start;
	
	while (current!=NULL) {
		
		dferrors_printdebug("[%d] %s  (%d)\n",i,current->value,current->length);
		
		current = current->next;
		i++;
	}
}